/**
 *  @file  vsStringUtils.h version 1.0 2007-11-20 
 *  @brief Definition of class StringUtility.
 *  Copyright (c) 2007 by @author <abitfree@gmail.com>
 */

#ifndef __VS_STRING_UTILS_H__
#define __VS_STRING_UTILS_H__

#include <string>
#include <sstream>
#include "vsDefines.h"

namespace vs
{

/**
 * @brief A utility class for std::string operations.
 *
 * All the utility functions are declared as static public.
 */
class StringUtility
{
	//typedef std::std::string std::string;

private:
	/// Disable instancing.
	StringUtility();
	
	/// Disable deleting.
	~StringUtility();

public:
	
    /// Delete the left spaces.
    static std::string TrimLeft(const std::string &strInput);

    /// Delete the right spaces.
    static std::string TrimRight(const std::string &strInput);

    /// Delete both the left and right spaces.
    static std::string Trim(const std::string &strInput);

	/// Repeat a std::string n times.
	static std::string Repeat(const std::string &str, int n);

    /// Transform a std::string to upper case.
    static std::string ToUpper(const std::string &strInput);

    /// Transform a std::string to lower case.
    static std::string ToLower(const std::string &strInput);

	/// Deciding a std::string is all spaces or not.
	static bool IsAllSpaces(const std::string &str);

    /// Deciding a std::string is all digits or not.
    static bool IsDigitString(const std::string &str);

    /// Compare two strings, ignoring the case.
    static int CompareIgnoreCase(const std::string &strLeft, const std::string &strRight);

	/// Deciding a std::string is end with a certain suffix or not.
	static bool EndWith(const std::string &str, const std::string &strSuffix);

	/// Deciding a std::string is start with a certain prefix or not.
	static bool StartWith(const std::string &str, const std::string &strPrefix);

	/// Replace all the sub-strings matching the pattern.
	static void ReplaceAll(std::string &str, const std::string &strOld, const std::string &strNew);

	/// The printf style formating helper for class std::string.
	static std::string Format(const char *pszFormat, ...);

	/// The printf style formating helper for class std::string.
	static std::string Format(const char *pszFormat, va_list &al);

	/// A template function, convert a std::string to built in types.
	template<typename T>
	static T StringToType(const std::string &str);

	/// Convert a std::string to bool type, .
	static bool StringToBool(const std::string &str);
	
	/// Convert a std::string to int type.
	static int StringToInt(const std::string &str);

	/// Convert a std::string to long type.
	static long StringToLong(const std::string &str);
	
	/// Convert a std::string to float type.
	static double StringToFloat(const std::string &str);

	/// Convert a std::string to double type.
	static double StringToDouble(const std::string &str);
	
	/// A template function, convert built in type to std::string class.
	template<typename T>
	static std::string ToString(T value);

	/**
	 * @brief A template function, split a std::string.
	 *
	 * T is the an container class, and it must have a interface push_back(const std::string &str).
	 */
	template<typename T>
	static int Split(const std::string &str, T &cont, const std::string &sep);
};

/////////////////////////////////////////////////////////////////////
// Member functions for class StringUtility.
/////////////////////////////////////////////////////////////////////

INLINE
bool StringUtility::EndWith(const std::string &str, const std::string &strSuffix)
{
	return (str.length() - str.rfind(strSuffix)) == strSuffix.length();
}

INLINE
bool StringUtility::StartWith(const std::string &str, const std::string &strPrefix)
{
	return (str.find(strPrefix) == 0);
}

INLINE
std::string StringUtility::Format(const char *pszFormat, ...)
{
	va_list al;
	va_start(al, pszFormat);
	std::string str = StringUtility::Format(pszFormat, al);
	va_end(al);

	return str;
}

template<typename T>
INLINE
T StringUtility::StringToType(const std::string &str)
{
	T value;
	std::istringstream istr(str) ;
	istr>>value;
	return value;
}

// convert bool to std::string "true" or "false"
template<>
INLINE 
bool StringUtility::StringToType<bool>(const std::string &str) 
{
	bool value;
	std::istringstream istr(str) ;
	istr>>std::boolalpha>>value;
	return value;
}

INLINE 
bool StringUtility::StringToBool(const std::string &str)
{
	return StringToType<bool>(str);
}

INLINE 
int StringUtility::StringToInt(const std::string &str)
{
	return StringToType<int>(str);
}

INLINE 
long StringUtility::StringToLong(const std::string &str)
{
	return StringToType<long>(str);
}

INLINE 
double StringUtility::StringToFloat(const std::string &str)
{
	return StringToType<float>(str);
}

INLINE 
double StringUtility::StringToDouble(const std::string &str)
{
	return StringToType<double>(str);
}

template<typename T>
INLINE 
std::string StringUtility::ToString(T value)
{
	std::ostringstream istr;
	istr<<value;
	return std::string(istr.str());
}

// convert bool to std::string "true" or "false"
template<>
INLINE 
std::string StringUtility::ToString(bool value) 
{
	std::ostringstream oss;
	oss<<std::boolalpha<<value;
	return oss.str();
}

std::string StringUtility::TrimLeft(const std::string &strInput)
{
	std::string str = strInput;
	std::string::iterator i = str.begin();
	for ( ; i!=str.end(); ++i) 
	{
		if (!isspace(*i)) 
		{
			break;
		}
	}

	if (i == str.end()) 
	{
		str.clear();
	} 
	else 
	{
		str.erase(str.begin(), i);
	}

	return str;
}

INLINE
std::string StringUtility::TrimRight(const std::string &strInput)
{
	if (strInput.begin() == strInput.end()) 
	{
		return strInput;
	}
	
	std::string str = strInput;
	for (std::string::iterator i=str.end() - 1; ; --i) 
	{
		if (!isspace(*i))
		{
			str.erase(i + 1, str.end());
			break;
		}
		if (i == str.begin()) 
		{
			str.clear();
			break;
		}
	}
	return str;
}

INLINE
std::string StringUtility::Trim(const std::string &strInput)
{
	std::string str = strInput;

	std::string::iterator i = str.begin();
	for (; i!=str.end(); ++i) 
	{
		if (!isspace(*i)) 
		{
			break;
		}
	}

	if (i == str.end())	// empty std::string
	{
		str.clear();
		return str;
	} 
	else 
	{
		str.erase(str.begin(), i);
	}

	// Here can be sure that str is not empty std::string.
	for (i = str.end() - 1; ; --i) 
	{
		if (!isspace(*i)) 
		{
			str.erase(i + 1, str.end());
			break;
		}
		if (i == str.begin()) 
		{
			str.clear();
			break;
		}
	}

	return str;
}

INLINE
std::string StringUtility::Repeat(const std::string& str, int n) 
{
	std::string s;
	for (int i = 0; i < n; ++i)
	{
		s += str;
	}
	return s;
}

INLINE
std::string StringUtility::ToUpper(const std::string &strInput)
{
	std::string str = strInput;
	for (std::string::iterator i=str.begin(); i!=str.end(); ++i) 
	{
		*i = std::toupper(*i);
	}
	return str;
}

INLINE
std::string StringUtility::ToLower(const std::string &strInput)
{
	std::string str = strInput;
	for (std::string::iterator i=str.begin(); i!=str.end(); ++i) 
	{
		*i = std::tolower(*i);
	}
	return str;
}

INLINE
bool StringUtility::IsAllSpaces(const std::string &str)
{
	for (std::string::const_iterator i=str.begin(); i!=str.end(); ++i) 
	{
		if (!isspace(*i))
		{
			return false;
		}
	}
	return true;
}

INLINE
bool StringUtility::IsDigitString(const std::string &str)
{
	for (std::string::const_iterator i=str.begin(); i!=str.end(); ++i) 
	{
		if (!isdigit(*i))
		{
			return false;
		}
	}
    return true;
}

INLINE
int StringUtility::CompareIgnoreCase(const std::string &strLeft, const std::string &strRight)
{
    std::string strUpperLeft = ToUpper(strLeft);
    std::string strUpperRight = ToUpper(strRight);
    
    if (strUpperLeft < strUpperRight)
    {
        return -1;
    }
    else if (strUpperLeft > strUpperRight)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

INLINE
void StringUtility::ReplaceAll(std::string &str, const std::string &strOld, const std::string &strNew)
{
	std::string::size_type pos;
	while ((pos = str.find(strOld)) != std::string::npos)
	{
		str.replace(pos, strOld.length(), strNew);
	}
}

INLINE
std::string StringUtility::Format(const char *pszFormat, va_list &al)
{
	int size = 1024;
	int n = 0;

	while (true)
	{
		char *pszBuf = new char[size];
		if (pszBuf == 0)
		{
			return std::string();
		}

		n = vsnprintf(pszBuf, size, pszFormat, al);

		if(n > -1 && n < size)
		{
			std::string str(pszBuf, n);
			delete [] pszBuf;
			return str;
		}

		if(n > -1)
		{
			size = n + 1;
		}
		else
		{
			size *= 2;
		}

		delete [] pszBuf;		
	}

	return std::string();
}

template<typename T>
INLINE
int StringUtility::Split(const std::string &str, T &cont, const std::string &sep)
{
	int count = 0;
	std::string::size_type offset = 0;
	std::string::size_type pos = 0;

	std::string substr;
	while ((pos=str.find(sep, offset)) != std::string::npos)
	{
		substr = str.substr(offset, pos - offset);
		cont.push_back(substr);

		offset = pos + sep.length();		
		count++;
	}

	substr = str.substr(offset, pos - offset);
	if (!substr.empty())
	{
		cont.push_back(substr);
		count++;
	}

	return count;
}

}

#endif // __VS_STRING_UTILS_H__
